using System;
using System.Collections.Generic;
using System.Text;
using JobShopModel.Model.FactoryLvl;
using JobShopModel.Model.Station;

namespace JobShopModel.Model.JobLvl
{
    /// <summary>
    /// Represent an Operation.
    /// Several operation represent a Job <seealso cref="Job"/>
    /// 
    /// Element recorded:
    /// The time waiting on the Queue of the station <see cref="WaitingTime"/>
    /// The time being operated by a station <see cref="OperationTime"/>
    /// The time waiting for a transporter after being done <see cref="BlockedTime"/>
    /// 
    /// States:
    /// DONE: when an operation have been excecuted by its assigned station
    /// UNDONE: when it have not been yet excecuted
    /// 
    /// </summary>
    public class Operation
    {
        private Time waitingTime;
        private Time serviceTime;
        private Time blockedTime;

        private Time sWaitingQueue;
        private Time sBlocked;

        private StationsName station;

        private OperationStates state;


        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="station">Station where the operation must be done</param>
        /// <param name="meanServiceTime">Excecution time of the operation</param>
        public Operation(StationsName station,Time serviceTime)
        {
            this.state = OperationStates.UNDONE;
            this.serviceTime = serviceTime;
            this.blockedTime = new Time();
            this.waitingTime = new Time();
            this.station = station;
            
        }

        #region Properties;

        /// <summary>
        /// Waiting time at the Queue of the Station
        /// </summary>
        public Time WaitingTime
        {
            get { return waitingTime; }
        }

        /// <summary>
        /// Time waiting for a transporter after being excecuted by a station
        /// </summary>
        public Time BlockedTime
        {
            get { return blockedTime; }
        }

        /// <summary>
        /// Time spent by the operation being excecuted
        /// </summary>
        public Time ServiceTime
        {
            get { return serviceTime; }
        }

        /// <summary>
        /// Station where the operation must be done
        /// Readonly, must be set by the constructor
        /// </summary>
        public StationsName Station
        {
            get { return this.station; }

        }

        /// <summary>
        /// State of the Operation.
        /// <seealso cref="OperationStates"/>
        /// </summary>
        /// <exception cref="Exception"> If illegal assignment of a value</exception>
        public OperationStates State
        {
            get { return state; }
        }

        #endregion Properties;

        /// <summary>
        /// Called when the operation cannot proceed because all machines are occupied
        /// updates statistics for this job type
        /// </summary>
        /// <param name="start">Time started waiting</param>
        public void setWaitingQueue(Time start)
        {
            //Can only start waiting if the job is currently being processed
            if (this.state == OperationStates.UNDONE)
            {
                this.sWaitingQueue = start;
                this.state = OperationStates.WAITING;
            }
            else
            {
                throw new Exception("Illegal Operation: Can not change from: " + this.state + " to Waiting");
            }
        }

        /// <summary>
        /// Called when a machine becomes available
        /// Updates statistics
        /// </summary>
        /// <param name="start">Time the operation started processing</param>
        public void setWorking(Time start)
        {
            //Can only start working if the job has been waiting
            if (this.state == OperationStates.WAITING)
            {
                this.waitingTime = (start - sWaitingQueue);
                this.state = OperationStates.WORKING;
            }
            else
            {
                throw new Exception("Illegal Operation: Can not change from: " + this.state + " to Working");
            }
        }

        /// <summary>
        /// Called when the operation finishes and is waiting for the transport
        /// Updates statistics
        /// </summary>
        /// <param name="start">Time the operation completed</param>
        public void setBlocked(Time start)
        {
            //Can only go to blocked if the job has finished working
            if (this.state == OperationStates.WORKING)
            {
                this.sBlocked = start;
                this.state = OperationStates.BLOCKED;

            }
            else
            {
                throw new Exception("Illegal Operation: Can not change from: " + this.state + " to Blocked");
            }
        }

        /// <summary>
        /// Called when the job is picked up by the transport
        /// Updates statistics
        /// </summary>
        /// <param name="start">Time of pickup</param>
        public void setDone(Time start)
        {
            //Can only go from blocked state to done
            if (this.state == OperationStates.BLOCKED)
            {
                this.blockedTime = start - sBlocked;
                this.state = OperationStates.DONE;
            }
            else
            {
                throw new Exception("Illegal Operation: Can not change from: " + this.state + " to Done");
            }
        }

        /// <summary>
        /// Called to terminate a job without proceeding through normal states
        /// </summary>
        public void Terminate()
        {
            if (this.state == OperationStates.UNDONE)
            {
                this.blockedTime = new Time();
                this.waitingTime = new Time();
                this.state = OperationStates.DONE;
            }
        }

        public override string ToString()
        {
            return "Operation(" + state + ")";
        }

    }

    /// <summary>
    /// States of an Operation
    /// </summary>
    public enum OperationStates 
    { 
        UNDONE,
        WAITING,
        WORKING,
        BLOCKED,
        DONE
    }

}
